#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
#include <ctype.h>
#include <iostream>

using namespace std;

#include "Parser_cplex.h"
 

//---------------------------------------------------------------------------

Parser_cplex::Parser_cplex ()
{
	lpEnv = NULL;
	lp = NULL;
	primalSol = NULL;
	dualSol = NULL;    
	reducedCost = NULL;
	ilpSol = NULL;
	kParam = 0;
	/*cmatbeg = new int[1];
	cmatind = new int[1];
	cmatval = new double[1];
	obj = new double[1];
	lb = new double[1];
	ub = new double[1];
	primalSol = new double [1];  
	reducedCost = new double [1];
	dualSol = new double [1];*/
}
//---------------------------------------------------------------------------

Parser_cplex::~Parser_cplex ()
{
	//delete [] dualSol;
	dualSol = NULL;
	Free_and_Null ((char **) &cmatbeg);
	Free_and_Null ((char **) &cmatind);
	Free_and_Null ((char **) &cmatval);
	Free_and_Null ((char **) &lb);
	Free_and_Null ((char **) &ub);
	Free_and_Null ((char **) &primalSol);
	Free_and_Null ((char **) &reducedCost);
	Free_and_Null ((char **) &obj);
	Free_and_Null ((char **) &dualSol);
	
}
//---------------------------------------------------------------------------
int Parser_cplex::getCplex ()
{	
	int status = 0;
	//** Initialize the CPLEX environment **
	// If an error occurs, the status value indicates the reason for  
	// failure.  A call to CPXgeterrorstring will produce the text of 
	// the error message.  Note that CPXopenCPLEX produces no output, 
	// so the only way to see the cause of the error is to use        
	// CPXgeterrorstring.  For other CPLEX routines, the errors will  
	// be seen if the CPX_PARAM_SCRIND indicator is set to CPX_ON.  
	
	srand (time (NULL));
		
	while (1) 
	{ 	                         
		lpEnv = CPXopenCPLEX (&status); 
		
		if (lpEnv != NULL)
			break;
		else
		{
			char errmsg[1024];
			fprintf (stderr, "Could not open CPLEX environment.\n");
			CPXgeterrorstring (lpEnv, status, errmsg);
			fprintf (stderr, "%s", errmsg);
			goto TERMINATE;
			int time = 450 + rand()%300;
			fprintf (stderr, "Wait %d seconds!!\n", time);
			Sleep (time);
		}
	}	      
	
	srand (0);
	
#ifndef CPLEX65	
	//** Turn on output to the screen **
	status = CPXsetintparam (lpEnv, CPX_PARAM_SCRIND, CPX_ON);
	if (status) 
	{
		fprintf (stderr, 
		         "Failure to turn on screen indicator, error %d.\n", status);
		goto TERMINATE;
	}	                
	
	
	//** Turn on data checking **
	status = CPXsetintparam (lpEnv, CPX_PARAM_DATACHECK, CPX_ON);
	if (status) 
	{
		fprintf (stderr, 
		         "Failure to turn on data checking, error %d.\n", status);
		goto TERMINATE;
	}
#endif	
	
	//* Create the problem **
	lp = CPXcreateprob (lpEnv, &status, "GR");	
	if (lp == NULL) 
	{
		fprintf (stderr, "Failed to create LP.\n");
		goto TERMINATE;
	}	     
	
	//CPXsetintparam(lpEnv, CPX_PARAM_THREADS , 1);

	  
TERMINATE:	
	
	return (status);
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
int Parser_cplex::CGR_CreateProblemLP ()
{	
	
	cout<<endl<<"create LP problem"<<endl;
	int status = 0;
	
	//** add new rows
	cout<<endl<<"add LP rows"<<endl;
	status = CGR_AddNewRows (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to add new rows.\n");
		goto TERMINATE;
	}	
    cout << "1.rows : " << CPXgetnumrows << "\t" << "cols : " << CPXgetnumcols << endl;
			//** add initial columns **
	
	cout<<endl<<"add of constraints to LP problem"<<endl;
	
	//** add initial columns **
	status = CGR_AddCoarseNetColumns (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to add initial columns.\n");
		goto TERMINATE;
	}		
    cout << "2.rows : " << CPXgetnumrows << "\t" << "cols : " << CPXgetnumcols << endl;
	
	status = CGR_AddGridEdgeColumns (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to add initial columns.\n");
		goto TERMINATE;
	}	 
    cout << "3.rows : " << CPXgetnumrows << "\t" << "cols : " << CPXgetnumcols << endl;
	
	//** add initial columns **
	status = CGR_AddSColumns (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to add initial columns.\n");
		goto TERMINATE;
	}		
    cout << "4.rows : " << CPXgetnumrows << "\t" << "cols : " << CPXgetnumcols << endl;
	
	//** add initial columns **
	status = CGR_AddTColumns (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to add initial columns.\n");
		goto TERMINATE;
	}		
    cout << "5.rows : " << CPXgetnumrows << "\t" << "cols : " << CPXgetnumcols << endl;
	
    exit (0);
	////////////////////////////////////////////////////////////////////////////
	
	 /*cstat = new int[parser.edgeCount2D+parser.snets.size()*2];
	 rstat = new int[parser.edgeCount2D+parser.snets.size()];
	 cur_numrows = CPXgetnumrows (lpEnv, lp);
	 for( i=0; i<cur_numrows; ++i)
	 		rstat[i] = CPX_BASIC;
	 for( i=0; i<parser.edgeCount2D; ++i)
	 	if(parser.edgeRCap[i]<0)
	 		cstat[i] = CPX_AT_UPPER;
	 	else
	 		cstat[i] = CPX_AT_LOWER;
	 cur_numcols = parser.edgeCount2D+parser.snets.size()*2;
	 for( i=parser.edgeCount2D; i<cur_numcols; i+=2){
	 	cstat[i] = CPX_AT_UPPER;
	 	cstat[i+1] = CPX_AT_LOWER;
		}
	 status = CPXcopybase (lpEnv, lp, cstat, rstat);
   if ( status ) {
      fprintf (stderr, "Failed to copy the basis.\n");
      goto TERMINATE;
   }
   delete [] cstat;
   delete [] rstat;
   */
   
   /////////////////////////////////////////////////////
	//** Optimize the initial problem and obtain solution **
	//if(cgr.resolution >1)
	status = CGR_SolveLP (lpEnv, lp);
	if (status) 
	{
		fprintf (stderr, "Failed to solve.\n");
		goto TERMINATE;
	}
	
	  
TERMINATE:	
	
	return (status);
}
//---------------------------------------------------------------------------

void Parser_cplex::DeleteProblemLP ()
{         
	int status;
	
	//** Free up the problem as allocated neighbors[3] CPXcreateprob, if necessary **
	if (lp != NULL ) 
	{
		status = CPXfreeprob (lpEnv, &lp);
		if (status) 
			fprintf (stderr, "CPXfreeprob failed, error code %d.\n", status);
	}
	
	 //** Free up the CPLEX environment, if necessary **
	if (lpEnv != NULL) 
	{
		status = CPXcloseCPLEX (&lpEnv);
		
		if (status > 0) 
		{
			char  errmsg[1024];
			fprintf (stderr, "Could not close CPLEX environment.\n");
			CPXgeterrorstring (lpEnv, status, errmsg);
			fprintf (stderr, "%s", errmsg);
		}
	}	
	
	//delete [] dualSol;
	dualSol = NULL;
	Free_and_Null ((char **) &cmatbeg);
	Free_and_Null ((char **) &cmatind);
	Free_and_Null ((char **) &cmatval);
	Free_and_Null ((char **) &lb);
	Free_and_Null ((char **) &ub);
	Free_and_Null ((char **) &primalSol);
	Free_and_Null ((char **) &reducedCost);
	Free_and_Null ((char **) &obj);
	Free_and_Null ((char **) &dualSol);
	
	
	lpEnv = NULL;
	lp = NULL;
	primalSol = NULL;
	dualSol = NULL;    
	reducedCost = NULL;
	ilpSol = NULL;

}
//---------------------------------------------------------------------------
void Parser_cplex::CGR_DeleteLP(void){
	
	CPXdelcols(lpEnv,lp,cgr.edgeCountOf,cgr.edgeCountOf+cgr.nets.size()-1);
	
}
//---------------------------------------------------------------------------    
int Parser_cplex::CGR_SolveLP (CPXENVptr env, CPXLPptr lp)
{
	int status;
	
	cout<<endl<<"solving LP problem"<<endl;
	
   
   //** set time limitation **
	status = CPXsetdblparam (lpEnv, CPX_PARAM_TILIM, 30);
	if (status) 
	{
		fprintf (stderr, "Failure to set time limitation.\n");
		exit(1);
	}			
	
	status = CPXsetintparam (lpEnv, CPX_PARAM_LPMETHOD , CPX_ALG_BARRIER);
	if (status) 
	{
		fprintf (stderr, 
		         "Failure to change the method, error %d.\n", status);
		exit(1);
	}	                
	
	
	status = CPXlpopt (env, lp);


	if (status) 
	{
		fprintf (stderr, "Failed to optimize LP.\n");
		exit(1);
	}	
	
	
	primalSol = new double [routeNum+cgr.edgeCount2D + cgr.regions.size()*2 + 1];  
	reducedCost = new double [routeNum+cgr.edgeCount2D + cgr.regions.size()*2 + 1];  

	
	status = CPXsolution (env, lp, &solStat, &objValue, 
	                      primalSol, NULL, NULL, reducedCost);
	
	uint32_t newIndex=0;
	for(uint32_t i=0; i<cgr.criticalCoarseNets.size(); ++i){
		uint32_t n = cgr.criticalCoarseNets[i];
		for(uint32_t r=0; r<cgr.nets[n].lpSols.size(); ++r){
			cgr.nets[n].lpSols[r] = primalSol[newIndex++];
		}
	}
	
	for(uint32_t i=0; i<cgr.edgeCount2D; ++i){
		cgr.edges[i].ds = primalSol[routeNum+i];
		//cgr.edges[i].ds = dualSol[routeNum+i];
	}
	
	
	
	delete [] primalSol;
	primalSol = NULL;
	delete [] reducedCost;
	reducedCost = NULL;
	if (status) 
	{
		fprintf (stderr, "Failed to obtain solution.\n");
	}		
	
	cout<<endl<<endl;
	
	return 0;
}
//---------------------------------------------------------------------------    

//---------------------------------------------------------------------------  
int Parser_cplex::CGR_AddNewRows (CPXENVptr env, CPXLPptr lp)
{
	//** allocate memory **
	
	int status; 
	int rcnt = cgr.criticalCoarseNets.size() + cgr.edgeCount2D + cgr.regions.size()*2;
	double * rhs = new double [rcnt];
	char * sense = new char [rcnt];
	//dualSol = new double [rcnt];
	//** get row information **
	for (uint32_t i = 0; i < cgr.criticalCoarseNets.size(); i++)
	{
		rhs[i] = 1.0;		
		sense[i] = 'E';			
	}
	
	uint32_t currIndex = cgr.criticalCoarseNets.size(); 
	
	for (uint32_t i = 0; i < cgr.edgeCount2D; i++)
	{
		rhs[currIndex+i] = cgr.edges[i].adjustedCapacity/2;				
		sense[currIndex+i] = 'L';	
	}		
	currIndex += cgr.edgeCount2D;
	for (uint32_t i = 0; i < cgr.regions.size(); i++)
	{
		rhs[currIndex+i] = 0;				
		sense[currIndex+i] = 'L';	
	}	  		
	
	currIndex += cgr.regions.size();
	for (uint32_t i = 0; i < cgr.regions.size(); i++)
	{
		rhs[currIndex+i] = 0;				
		sense[currIndex+i] = 'L';	
	}
	
	
				
	//** add rows to LP **
	
		
	status = CPXnewrows (env, lp, rcnt, rhs, sense, NULL, NULL);
	                 
	delete [] rhs;
	rhs = NULL;
	delete [] sense;
	sense = NULL;
	
	return (status);	
}
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------  

int Parser_cplex::CGR_AddCoarseNetColumns (CPXENVptr env, CPXLPptr lp)
{
	//** allocate memory **
	cout<<endl<<"add net column constraints to LP problem"<<endl;
	int status; 
	int ccnt=0; 
	int nzcnt=0;
	for(uint32_t i=0; i<cgr.criticalCoarseNets.size(); ++i)
		ccnt += cgr.nets[cgr.criticalCoarseNets[i]].finalRouteVector.size();
		
	nzcnt = 0;
	routeNum = 0;
	for (uint32_t i = 0; i < cgr.criticalCoarseNets.size(); i++)
	{
		uint32_t n = cgr.criticalCoarseNets[i];
		routeNum += cgr.nets[n].finalRouteVector.size();
		for(uint32_t r=0; r<cgr.nets[n].finalRouteVector.size(); ++r){
			nzcnt++;
			nzcnt += cgr.nets[n].finalRouteVector[r].size();
		}
	}
	
	
	cmatbeg = new int [ccnt +1]; 
	cmatind = new int [nzcnt];
	cmatval = new double [nzcnt];
	lb = new double [nzcnt];
	ub = new double [nzcnt];
	obj = new double [nzcnt];
	//** get information of new columns **

	
	cmatbeg[0] = 0;
	ccnt = 0;
	nzcnt = 0;	
	int wl=0;
	
	for (uint32_t i = 0; i < cgr.criticalCoarseNets.size(); i++)
	{
		uint32_t n = cgr.criticalCoarseNets[i];
		for(uint32_t r=0; r<cgr.nets[n].finalRouteVector.size(); ++r){
			
			cmatind[nzcnt] = i;
			cmatval[nzcnt] = 1.0;
			nzcnt += 1;
			wl = cgr.nets[n].finalRouteVector[r].size();
			
			for(GridEdgeSet::iterator it = cgr.nets[n].finalRouteVector[r].begin();
	        it != cgr.nets[n].finalRouteVector[r].end(); ++it){
	        	if (it->first < cgr.edgeCount2D)
					{
						cmatind[nzcnt] = it->first + cgr.criticalCoarseNets.size();
						cmatval[nzcnt] = 1.0;
						nzcnt += 1;
					}
	        
			}
			
			obj[ccnt] = 0;
			// bounds
			lb[ccnt]			= 0.0;
			ub[ccnt]			= 1.0;
			
			cmatbeg[++(ccnt)] = nzcnt;	
		}
		
	}	
	
	//** add new columns to LP **
	status = CPXaddcols (env, lp, ccnt, nzcnt, NULL, 
	                     cmatbeg, cmatind, cmatval, lb, ub, NULL);
	
	delete [] obj;
	obj = NULL;
	delete [] cmatbeg;
	cmatbeg = NULL;
	delete [] cmatind;
	cmatind = NULL;
	delete [] cmatval;
	cmatval = NULL;
	delete [] lb;
	lb = NULL;
	delete [] ub;
	ub = NULL;
	
	return (status);
}
//---------------------------------------------------------------------------  

//---------------------------------------------------------------------------  

int Parser_cplex::CGR_AddGridEdgeColumns (CPXENVptr env, CPXLPptr lp)
{
	//** allocate memory **
	int status; 
	int ccnt; 
	int nzcnt;
	double * obj;
	int * cmatbeg;
	int * cmatind;
	double * cmatval;
	double * lb;
	double * ub;
	
	cout<<endl<<"add edge column constraints to LP problem"<<endl;
	
	ccnt =  cgr.edgeCount2D;
	nzcnt = (cgr.edgeCount2D+1)*3;
		
	obj = new double [ccnt];
	cmatbeg = new int [nzcnt+1];
	cmatind = new int [nzcnt];
	cmatval = new double [nzcnt];
	lb = new double [nzcnt];
	ub = new double [nzcnt];
	
	//** get information of new columns **

	cmatbeg[0] = 0;
	ccnt = 0;
	nzcnt = 0;		
	
	for(uint32_t i=0; i<cgr.edgeCount2D; ++i){				
		// wire constraint
		//if(cgr.edges[i].currentUtil <= cgr.edges[i].cap)
			//continue;
		cgr.edgeCountOf +=1;
			
		cmatind[nzcnt] = cgr.criticalCoarseNets.size()+i;
		cmatval[nzcnt] = -1;
		nzcnt += 1;		
		if(cgr.edges[i].region >= 0){
			cmatind[nzcnt] = cgr.criticalCoarseNets.size()+cgr.edgeCount2D+cgr.edges[i].region;
			cmatval[nzcnt] = 1;
			nzcnt += 1;	
			cmatind[nzcnt] = cgr.criticalCoarseNets.size()+cgr.edgeCount2D+cgr.regions.size()+cgr.edges[i].region;
			cmatval[nzcnt] = 1;
			nzcnt += 1;	
		}
		obj[ccnt]			= 0;		
		// bounds
		lb[ccnt]			= 0.0;
		ub[ccnt]			= 100000.0;		
		cmatbeg[++(ccnt)] = nzcnt;		 
		
	}		
	status = CPXaddcols (env, lp, ccnt, nzcnt, obj, 
	                     cmatbeg, cmatind, cmatval, lb, NULL, NULL);	
	//** release memory **
	
	delete [] obj;
	obj = NULL;
	delete [] cmatbeg;
	cmatbeg = NULL;
	delete [] cmatind;
	cmatind = NULL;
	delete [] cmatval;
	cmatval = NULL;
	delete [] lb;
	lb = NULL;
	delete [] ub;
	ub = NULL;
	return (status);
}

//---------------------------------------------------------------------------  

int Parser_cplex::CGR_AddTColumns (CPXENVptr env, CPXLPptr lp)
{
	//** allocate memory **
	int status; 
	int ccnt; 
	int nzcnt;
	double * obj;
	int * cmatbeg;
	int * cmatind;
	double * cmatval;
	double * lb;
	double * ub;
	
	cout<<endl<<"add T column constraints to LP problem"<<endl;
	
	ccnt =  1;
	nzcnt = cgr.regions.size()+1;
		
	obj = new double [ccnt];
	cmatbeg = new int [nzcnt+1];
	cmatind = new int [nzcnt];
	cmatval = new double [nzcnt];
	lb = new double [nzcnt];
	ub = new double [nzcnt];
	
	//** get information of new columns **

	cmatbeg[0] = 0;
	ccnt = 0;
	nzcnt = 0;		
	
	for(uint32_t i=0; i<cgr.regions.size(); ++i){				
					
		cmatind[nzcnt] = cgr.criticalCoarseNets.size()+ cgr.edgeCount2D + i;
		cmatval[nzcnt] = -1;
		nzcnt += 1;		
	}
	obj[ccnt]			= kParam;		
	// bounds
	lb[ccnt]			= 0.0;
	ub[ccnt]			= 100000.0;		
	cmatbeg[++(ccnt)] = nzcnt;		 
	
		
	status = CPXaddcols (env, lp, ccnt, nzcnt, obj, 
	                     cmatbeg, cmatind, cmatval, lb, NULL, NULL);	
	//** release memory **
	
	delete [] obj;
	obj = NULL;
	delete [] cmatbeg;
	cmatbeg = NULL;
	delete [] cmatind;
	cmatind = NULL;
	delete [] cmatval;
	cmatval = NULL;
	delete [] lb;
	lb = NULL;
	delete [] ub;
	ub = NULL;
	return (status);
}

//---------------------------------------------------------------------------  

int Parser_cplex::CGR_AddSColumns (CPXENVptr env, CPXLPptr lp)
{
	//** allocate memory **
	int status; 
	int ccnt; 
	int nzcnt;
	double * obj;
	int * cmatbeg;
	int * cmatind;
	double * cmatval;
	double * lb;
	double * ub;
	
	cout<<endl<<"add S column constraints to LP problem"<<endl;
	
	ccnt =  cgr.regions.size();
	nzcnt = cgr.regions.size()+1;
		
	obj = new double [ccnt];
	cmatbeg = new int [nzcnt+1];
	cmatind = new int [nzcnt];
	cmatval = new double [nzcnt];
	lb = new double [nzcnt];
	ub = new double [nzcnt];
	
	//** get information of new columns **

	cmatbeg[0] = 0;
	ccnt = 0;
	nzcnt = 0;		
	
	for(uint32_t i=0; i<cgr.regions.size(); ++i){				
					
		cmatind[nzcnt] = cgr.criticalCoarseNets.size()+ cgr.edgeCount2D + cgr.regions.size() + i;
		cmatval[nzcnt] = -1;
		nzcnt += 1;		
		obj[ccnt]			= 1.0 - kParam;		
		// bounds
		lb[ccnt]			= 0.0;
		ub[ccnt]			= 100000.0;		
		cmatbeg[++(ccnt)] = nzcnt;		 
		
	}		
	status = CPXaddcols (env, lp, ccnt, nzcnt, obj, 
	                     cmatbeg, cmatind, cmatval, lb, NULL, NULL);	
	//** release memory **
	
	delete [] obj;
	obj = NULL;
	delete [] cmatbeg;
	cmatbeg = NULL;
	delete [] cmatind;
	cmatind = NULL;
	delete [] cmatval;
	cmatval = NULL;
	delete [] lb;
	lb = NULL;
	delete [] ub;
	ub = NULL;
	return (status);
}

//---------------------------------------------------------------------------  

void Parser_cplex::Free_and_Null (char **ptr)
{
	if ( *ptr != NULL ) 
	{
		free (*ptr);
		*ptr = NULL;
	}
}
//---------------------------------------------------------------------------  

void Parser_cplex::Sleep (int sleeptime)
{
	int sec10 = sleeptime / 10;
	clock_t endsleep;
	
	for (int i = 0; i < sec10; i++)
	{
  	endsleep = clock () + 10 * CLOCKS_PER_SEC ;
  	while (clock() < endsleep)
  	{

  	}
  }
}
//---------------------------------------------------------------------------  

bool Parser_cplex::GetSelfLicense ()
{
	FILE * fp;
	int tmp;
	fp = fopen ("/pong/usr3/t/taihsuan/routing/license/cplex_license.txt", "r");
	
	if (fp == NULL)
	{
		fp = fopen ("/pong/usr3/t/taihsuan/routing/license/cplex_license.txt", "w");
		fprintf (fp, "1\n");
		fprintf (stderr, "cplex used license now : 1\n");
		fclose (fp);
		return true; 
	}	
	
	int licenseNow;
	tmp = fscanf (fp, "%d\n", &licenseNow);
	fclose (fp);
	
	if (licenseNow >= 7)
		return false;
	else
	{
		licenseNow += 1;
		
		fp = fopen ("/pong/usr3/t/taihsuan/routing/license/cplex_license.txt", "w");
		fprintf (fp, "%d\n", licenseNow);
		fprintf (stderr, "cplex used license now : %d\n", licenseNow);
		fclose (fp);
		return true; 		
	}
}
//---------------------------------------------------------------------------  

void Parser_cplex::ReleaseSelfLicense ()
{
	FILE * fp;
	int tmp;
	
	fp = fopen ("/pong/usr3/t/taihsuan/routing/license/cplex_license.txt", "r");
	
	if (fp == NULL)
	{
		return; 
	}	
	
	int licenseNow;
	tmp = fscanf (fp, "%d\n", &licenseNow);
	fclose (fp);
	
	licenseNow -= 1;
	
	if (licenseNow < 0 )
		licenseNow = 0;
		
	fp = fopen ("/pong/usr3/t/taihsuan/routing/license/cplex_license.txt", "w");
	fprintf (fp, "%d\n", licenseNow);
	fprintf (stderr, "cplex used license now : %d\n", licenseNow);
	fclose (fp);	
}
//---------------------------------------------------------------------------  

